/*
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */


#include "version.h"
#include "stdinc.h"
#include "AutoUpdate.h"
#include "..\client\Util.h"
#include "..\client\ResourceManager.h"
#include "..\client\LogManager.h"
#include "..\client\file.h"
#include "ZUtils.h"
#include "BZUtils.h"
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include "ViewlinkDCKey.h"
#include "InetDownloaderReporter.h"




AutoUpdate::~AutoUpdate()
{
		SettingsManager::getInstance()->removeListener(this);
		TimerManager::getInstance()->removeListener(this);
}

void 
AutoUpdate::shutdown()
{
    addTask(SHUTDOWN, 0); 
}


void	
AutoUpdate::initialize(HWND mainFrameHWND, AutoUpdateGUIMethod* guiDelegate)
{
	_mainFrameHWND = mainFrameHWND;
	m_guiDelegate = guiDelegate;
	SettingsManager::getInstance()->addListener(this);
	TimerManager::getInstance()->addListener(this);
	if (BOOLSETTING(AUTOUPDATE_ENABLE) && BOOLSETTING(AUTOUPDATE_RUNONSTARTUP))
		StartUpdate();
}

void AutoUpdate::on(TimerManagerListener::Minute, uint64_t tick) noexcept {
	if( BOOLSETTING(AUTOUPDATE_ENABLE) && BOOLSETTING(AUTOUPDATE_STARTATTIME)) {
		int hourUpdate = SETTING(AUTOUPDATE_TIME);
		if (hourUpdate >= 0 && hourUpdate < 24) // TODO please use SettingsManager::set(IntSetting key, int value)
		{
			// Check Current Time
			time_t currentTime = GET_TIME();
			tm*  currentLocalTime = localtime(&currentTime);
			if (currentLocalTime && currentLocalTime->tm_hour == hourUpdate && currentLocalTime->tm_min == 0)
			{
				StartUpdate();
			}
		}		
    }
}


void AutoUpdate::StartUpdate(bool p_manualUpdate/* = false*/)
{
	m_manualUpdate = p_manualUpdate;
	if (p_manualUpdate || BOOLSETTING(AUTOUPDATE_ENABLE))
		addTask(START_UPDATE, 0);
}


int 
AutoUpdate::run() {	

	while(true) {
        try {
        	if(!checkEvents())
        		break;
        } 
        catch(const Exception& e) {
			fail(e.getError());
        }
    }
	return 0;
}


bool 
AutoUpdate::checkEvents() {
    while(taskSem.wait()) {
		pair<Tasks, TaskData*> p;
		p.second = 0;
		{
			Lock l(cs);
			dcassert(m_tasks.size() > 0);
			if(m_tasks.size() == 0)  return false; 
			p = m_tasks.front();
			m_tasks.pop_front(); 
        }
		try {
			if(failed && p.first != SHUTDOWN) {
				dcdebug("AutoUpdate: New command when already failed: %d\n", p.first);
				fail(STRING(DISCONNECTED));
				delete p.second;
				continue;
			}

			switch(p.first) {
				case START_UPDATE:
                    {
						if (!IsUpdateStarted())
							startUpdateThisThread();

                    }break;
				case SHUTDOWN:
					return false;
            }

			delete p.second;
		} catch(const Exception& e) {
			delete p.second;
			fail(e.getError());
		}
    }
	return true;
}

void AutoUpdate::startUpdateThisThread()
{
	if (isUpdateStarted)
		return;
	cs.lock();
	if (isUpdateStarted) // double check
	{
		cs.unlock();
		return;
	}
	isUpdateStarted = true;
	cs.unlock();

	InetDownloadReporter::getInstance()->ReportResultWait(0);

	// Update here
	// UPDATE_FILELOAD
	// string serverURL = SETTING(AUTOUPDATE_SERVER_URL);
	string serverURL = getAUTOUPDATE_SERVER_URL();
	if (!serverURL.empty())
	{
		if (serverURL.length() && ( serverURL[ serverURL.length() - 1 ] != '/') )
				serverURL += '/';
		string serverUpdateFile = serverURL + UPDATE_FILEDOWNLOAD();
		string serverUpdateFileSign = serverURL + UPDATE_SIGNFILEDOWNLOAD();
		string serverUpdateDescription = serverURL + UPDATE_DESCRIPTION();

		boost::shared_array<byte> signData;
		size_t signDataSize = 0;

		string data;
		size_t dataSize =  Util::getDataFromInet(T_VERSIONSTRING, 4096, serverUpdateFile,data);
		if (dataSize)
		{
			signDataSize = Util::getDataFromInet(T_VERSIONSTRING, 4096, serverUpdateFileSign, signData);
			if (!verifyUpdate(signData.get(), signDataSize, data, dataSize))
			{
				LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_ERROR_VERIF));
				dataSize = 0;
			}
		}
		if (dataSize)
		{		
			LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_DOWNLOAD_SUCCESS));
			XMLParser::XMLResults xRes;
			const wstring wstr = Text::acpToWide(data);
			// Try to parse data
			XMLParser::XMLNode xRootNode = XMLParser::XMLNode::parseString(wstr.c_str(), 0, &xRes);
			if (xRes.error == XMLParser::eXMLErrorNone)
			{
				XMLParser::XMLNode update5Node = xRootNode.getChildNode(L"Update5");
				if (!update5Node.isEmpty())
				{
					boost::shared_ptr<AutoUpdateObject> autoUpdateObject = boost::shared_ptr<AutoUpdateObject>(new AutoUpdateObject());
					wstring wVersion = update5Node.getAttributeOrDefault(L"Version");
					wstring wDate = update5Node.getAttributeOrDefault(L"UpdateDate");
					autoUpdateObject.get()->m_sVersion = Text::wideToAcp(wVersion);
					autoUpdateObject.get()->m_sUpdateDate = Text::wideToAcp(wDate);
					// Let's asks for updater....
					int k = 0;
					XMLParser::XMLNode uNode = update5Node.getChildNode(L"Updater", &k);
					if (!uNode.isEmpty())
					{
						int j = 0;
						XMLParser::XMLNode fileNode = uNode.getChildNode(L"File", &j);
						while (!fileNode.isEmpty())
						{
							autoUpdateObject.get()->m_updater.m_Files.push_back(AutoUpdate::ParseNode(fileNode));
							fileNode = uNode.getChildNode(L"File", &j);
						}
					}

					int i = 0;
					XMLParser::XMLNode moduleNode = update5Node.getChildNode(L"Module", &i);
					while (!moduleNode.isEmpty())
					{
						AutoUpdateModule module;
						wstring wTitle = moduleNode.getAttribute(L"Title");
						module.m_sName = Text::wideToAcp(wTitle);
						module.m_iSetting = GetSettingByTitle(wTitle);
						
						int j = 0;
						XMLParser::XMLNode fileNode = moduleNode.getChildNode(L"File", &j);
						while (!fileNode.isEmpty())
						{
							module.m_Files.push_back(AutoUpdate::ParseNode(fileNode));
							fileNode = moduleNode.getChildNode(L"File", &j);
						}
						autoUpdateObject.get()->m_Modules.push_back(module);
						moduleNode = update5Node.getChildNode(L"Module", &i);					
					}

					// Process with Object
					// check what files to update....
					AutoUpdateFiles file4Update;
					AutoUpdateFiles file4UpdateInSettings;
					if (SETTING(AUTOUPDATE_IGNORE_VERSION) != autoUpdateObject.get()->m_sVersion)
					{
						for (size_t i = 0; i<autoUpdateObject.get()->m_Modules.size(); i++)
						{
							if ( SettingsManager::getInstance()->getBool(autoUpdateObject.get()->m_Modules[i].m_iSetting, true) )
							{
								for (size_t j = 0; j<autoUpdateObject.get()->m_Modules[i].m_Files.size(); j++)
								{
									AutoUpdateFile file = autoUpdateObject.get()->m_Modules[i].m_Files[j];
									if (NeedUpdateFile(file, Util::getExePath()))
									{
										file4Update.push_back(file);
									}
								}
							}
						}
					}else
					{
						LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_IGNORED));
					}
					if (file4Update.size() > 0)
					{
						// Check if Need to Update UpdateUpdater
						for (size_t j=0; j<autoUpdateObject.get()->m_updater.m_Files.size(); j++)
						{
							AutoUpdateFile file = autoUpdateObject.get()->m_updater.m_Files[j];
							if (NeedUpdateFile(file, Util::getConfigPath()))
							{
								file4UpdateInSettings.push_back(file);
							}
						}


						int64_t totalSize = 0;
						for (size_t i = 0; i<file4Update.size(); i++)
							totalSize += file4Update[i].m_packedSize;
						for (size_t i = 0; i<file4UpdateInSettings.size(); i++)
							totalSize += file4UpdateInSettings[i].m_packedSize;
						totalSize = totalSize;
						// Create TempFolder
						static string g_tempFolder = GetTempFolderForUpdate(autoUpdateObject.get()->m_sVersion);

						// Start Update (show info if need?)
						bool needToUpdate = true;
						string message = CSTRING(AUTOUPDATE_DOWNLOAD_START1);
						message += " " + autoUpdateObject.get()->m_sVersion;
						message += " ("+autoUpdateObject.get()->m_sUpdateDate+") ";
						message += CSTRING(AUTOUPDATE_DOWNLOAD_START2);
						message += Util::formatBytes( totalSize ) +" (";
						message += Util::toString(file4Update.size() + file4UpdateInSettings.size()) + " " + CSTRING(FILES2) + "). ";
						LogManager::getInstance()->message(message);
						if (BOOLSETTING(AUTOUPDATE_SHOWUPDATEREADY) || m_manualUpdate)
						{
							// [!]TODO Ask to setup
							// "FlylinkDC++ found new update %s ( %s ) and ready to download - %s. Continue this update?"
							message += CSTRING(AUTOUPDATE_DOWNLOAD_START3);
							// 
							UpdateResult idResult;
							if (m_guiDelegate != NULL)
							{
								// Download RTF file from Server
								string rtfData;
								size_t dataRTFSize =  Util::getDataFromInet(T_VERSIONSTRING, 4096, serverUpdateDescription, rtfData);
								if (dataRTFSize == 0)
									rtfData = Util::emptyString;
								idResult = (UpdateResult)m_guiDelegate->ShowDialogUpdate(message, rtfData);

							}else
							{
								UINT iResult = ::MessageBox(_mainFrameHWND, Text::utf8ToWide(message).c_str(), CWSTRING(AUTOUPDATE_TITLE), MB_YESNOCANCEL | MB_ICONQUESTION );
								switch (iResult)
								{
								case IDYES:
									idResult = UPDATE_NOW; break;
								case IDNO:
									idResult = UPDATE_IGNORE; break;
								case IDCANCEL:
								default:
									idResult = UPDATE_CANCEL;
								}
							}
							needToUpdate = idResult == UPDATE_NOW;
							if (!needToUpdate)
							{
								if (idResult == UPDATE_IGNORE)
								{
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_IGNORED) + " " + autoUpdateObject.get()->m_sVersion + " ("+autoUpdateObject.get()->m_sUpdateDate+")");
									SET_SETTING(AUTOUPDATE_IGNORE_VERSION, autoUpdateObject.get()->m_sVersion.c_str());
									SettingsManager::getInstance()->save();
								}else
								{
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_TEMP_IGNORE) + " " + autoUpdateObject.get()->m_sVersion + " ("+autoUpdateObject.get()->m_sUpdateDate+")");
								}
							}
						}
						if (needToUpdate)
						{
							// Start file Uploading
							InetDownloadReporter::getInstance()->ReportResultWait(totalSize);
							bool updateReady = true;
							string l_fileName;
							// Update Settings file at first
							for (size_t i = 0; i<file4UpdateInSettings.size(); i++)
							{
								LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(LOADING_FILE) + " " + file4UpdateInSettings[i].m_sName);
								if ( !PrepareFile(file4UpdateInSettings[i], Util::getConfigPath()) )
								{
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_ERROR_UPDATE_SETTING) + " " + file4UpdateInSettings[i].m_sName);
									if (BOOLSETTING(AUTOUPDATE_SHOWUPDATEREADY) || m_manualUpdate)
											l_fileName = file4UpdateInSettings[i].m_sName;
									updateReady = false;
									break;
								}else
								{
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + file4UpdateInSettings[i].m_sName + " " + STRING(AUTOUPDATE_SUCCESS));
								}
							}
							if (updateReady)
							{
								for (size_t i = 0; i<file4Update.size(); i++)
								{
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(LOADING_FILE) + " " + file4Update[i].m_sName);
									if ( !PrepareFile(file4Update[i], g_tempFolder) )
									{
										LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_ERROR_UPDATE_FILE) + " " + file4Update[i].m_sName);
										updateReady = false;
										if (BOOLSETTING(AUTOUPDATE_SHOWUPDATEREADY) || m_manualUpdate)
											l_fileName = file4Update[i].m_sName;
										break;
									}else
									{
										LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + file4Update[i].m_sName + " " + STRING(AUTOUPDATE_READY));
									}
								}
							}
							if (updateReady)
							{
								// [!]TODO Start Update Procedure.
								bool startUpdate = true;
								if (BOOLSETTING(AUTOUPDATE_SHOWUPDATEREADY) || m_manualUpdate)
								{
									wstring wMessage = CWSTRING(AUTOUPDATE_DOWNLOAD_FINISHED);
									if (BOOLSETTING(AUTOUPDATE_FORCE_RESTART)){
										wMessage+= CWSTRING(AUTOUPDATE_DOWNLOAD_RESTART);
										startUpdate = ::MessageBox(_mainFrameHWND, wMessage.c_str(), CWSTRING(AUTOUPDATE_TITLE), MB_YESNO | MB_ICONQUESTION) == IDYES;							
										if (!startUpdate)
										{
											LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_CANCEL));
										}
									}else
									{
										startUpdate = true;
										wMessage+= CWSTRING(AUTOUPDATE_DOWNLOAD_UPDATEONEXIT);
										::MessageBox(_mainFrameHWND, wMessage.c_str(), CWSTRING(AUTOUPDATE_TITLE), MB_OK);							
									}
								}
								
								if (startUpdate)
								{
									string flagName = g_tempFolder;
									AppendPathSeparator(flagName);
									flagName += UPDATE_FILE_NAME;
									File f( flagName, File::WRITE, File::CREATE | File::TRUNCATE );
									f.close();

									SET_SETTING(AUTOUPDATE_PATH_WITH_UPDATE, g_tempFolder.c_str());
									SettingsManager::getInstance()->save();

									::SendMessageA(_mainFrameHWND, WM_AUTO_UPDATE_RESTART, (WPARAM)&g_tempFolder, NULL);
									LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(AUTOUPDATE_STARTED));
								}
							}else
							{
								if (BOOLSETTING(AUTOUPDATE_SHOWUPDATEREADY) || m_manualUpdate)
								{
									string message = STRING(AUTOUPDATE_DOWNLOAD_FAILED) + "\r\n" + STRING(FAILED_TO_LOAD) + "\r\n" + serverURL + l_fileName;
									::MessageBox(_mainFrameHWND, Text::toT(message).c_str() , CWSTRING(AUTOUPDATE_TITLE), MB_OK | MB_ICONERROR);							
								}
							}
						}
					}
					else
					{
						LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(YOU_HAVE_THE_LATEST_VERSION));
						if (m_manualUpdate)
						{
							::MessageBox(_mainFrameHWND, CWSTRING(YOU_HAVE_THE_LATEST_VERSION), CWSTRING(AUTOUPDATE_TITLE), MB_OK | MB_ICONINFORMATION);
						}
					}
				}
			}


		}
	}
	cs.lock();
	isUpdateStarted = false;
	cs.unlock();
}

void
AutoUpdate::fail(const string& aError) {
	if(!failed) {
		failed = true;
		fire(AutoUpdateListener::Failed(), aError);
	}
}

SettingsManager::IntSetting
AutoUpdate::GetSettingByTitle(const wstring& wTitle)
{
	if (wTitle == L"exe")
		return SettingsManager::AUTOUPDATE_EXE;
	if (wTitle == L"utilities")
		return SettingsManager::AUTOUPDATE_UTILITIES;
	if (wTitle == L"lang")
		return SettingsManager::AUTOUPDATE_LANG;
	if (wTitle == L"portalbrowser")
		return SettingsManager::AUTOUPDATE_PORTALBROWSER;
	if (wTitle == L"emopacks")
		return SettingsManager::AUTOUPDATE_EMOPACKS;
	if (wTitle == L"webserver")
		return SettingsManager::AUTOUPDATE_WEBSERVER;
	if (wTitle == L"sounds")
		return SettingsManager::AUTOUPDATE_SOUNDS;
	if (wTitle == L"iconthemes")
		return SettingsManager::AUTOUPDATE_ICONTHEMES;
	if (wTitle == L"colorthemes")
		return SettingsManager::AUTOUPDATE_COLORTHEMES;
	if (wTitle == L"documentation")
		return SettingsManager::AUTOUPDATE_DOCUMENTATION;
	/*
	if (wTitle == L"chatbot")
		return SettingsManager::AUTOUPDATE_UPDATE_CHATBOT;
	*/
	return SettingsManager::AUTOUPDATE_UPDATE_UNKNOWN;
}

bool 
AutoUpdate::NeedUpdateFile(AutoUpdateFile& file, const string& outputFolder)
{
	if (file.m_sys == AutoUpdateFile::OsUnknown)
		return false;
#if defined(_WIN64) 
	if (file.m_sys == AutoUpdateFile::x86)
		return false;
#elif defined(_WIN32)
	if (file.m_sys == AutoUpdateFile::x64)
		return false;
#endif
	// Find file. If not found -> return true;
	//			  if found. Check size, if not equal return true;
	//									if equal. Check TTH. If not equal return true;
	//																	  return false;
	string filePath = outputFolder; //Util::getExePath();
	AppendPathSeparator(filePath);
	filePath+=file.m_sPath;
	AppendPathSeparator(filePath);
	filePath+=file.m_sName;

	int64_t outFileSize = 0;

	FileFindIter end;
	FileFindIter fiter(filePath);
	if ( fiter != end)
	{
		if (fiter->getSize() == file.m_size)
		{
			// Calculate TTH (in string)
			const int c_size_buf = 1024 * 1024;
			boost::shared_ptr<TigerTree>  tth;
			if (Util::getTTH_MD5( filePath, c_size_buf, &tth))
			{
				const string l_TTH_str = tth.get()->getRoot().toBase32();
				if ( l_TTH_str == file.m_sTTH)
					return false;
			}
		
		}
	}


	return true;
}

bool 
AutoUpdate::PrepareFile(AutoUpdateFile& file, const string& tempFolder)
{
	// string filenameX = "H:\\Projects\\viewlinkdc500\\compiled\\ViewlinkDC.exe";
	/*
	File fx(filenameX, File::READ, File::OPEN);
	int64_t fileXSize = fx.getSize();
	boost::shared_array<byte> dataIn = boost::shared_array<byte>(new byte[fileXSize]);
	boost::shared_array<byte> dataOut = boost::shared_array<byte>(new byte[fileXSize]);
	fx.read(dataIn.get(), (size_t&)fileXSize);
	fx.close();
	size_t inSize = fileXSize;
	size_t outSize = fileXSize;
	BZFilter bzip;
	if ( bzip(dataIn.get(), inSize, dataOut.get(), outSize) )
	{

		boost::shared_array<byte> dataOutTest = boost::shared_array<byte>(new byte[fileXSize]);
		UnBZFilter unbzip;
		unbzip(dataOut.get(), outSize, dataOutTest.get(), inSize);
		File fy(filenameX+".bz2", File::WRITE, File::OPEN | File::CREATE);
		fy.write( dataOut.get(), outSize);
		fy.close();
	}
	*/

	if (file.m_sDownloadURL.empty())
		return false;
	boost::shared_array<byte> data;
	IDateReceiveReporter* reporter = InetDownloadReporter::getInstance();
	int64_t sizeRead = Util::getDataFromInet(T_VERSIONSTRING, 4096, file.m_sDownloadURL, data, 0, reporter);
	if (sizeRead == file.m_packedSize)
	{
		/*
		File fy(filenameX+".bz2T", File::WRITE, File::OPEN | File::CREATE);
		fy.write( data.get(), sizeRead);
		fy.close();
		*/
		// Unpack if need
		size_t outSize = file.m_size+1;
		boost::shared_array<byte> out = boost::shared_array<byte>(new byte[file.m_size]);
		bool unPacked = false;
		if (file.m_packer == AutoUpdateFile::Zip)
		{
			UnZFilter unzip;
			unPacked  = ( unzip(data.get(), (size_t &)sizeRead, out.get(), outSize) && outSize == file.m_size);
		}else if (file.m_packer == AutoUpdateFile::BZip2)
		{
			UnBZFilter unbzip;
			try{
				unbzip(data.get(), (size_t &)sizeRead, out.get(), outSize);
				unPacked = ( outSize == file.m_size) ;
			}catch (Exception)
			{
				// Unpack error
				unPacked = false;
			}

		}else if (file.m_packer == AutoUpdateFile::Unpacked)
		{
			memcpy(out.get(), data.get(), sizeRead);
			unPacked = true;
		}
		if (unPacked)
		{
			// Save out.get() to File
			string destinationPath = tempFolder;
			AppendPathSeparator(destinationPath);
			destinationPath += file.m_sPath;
			AppendPathSeparator(destinationPath);
			destinationPath += file.m_sName;
			wstring wdestinationPath = Text::acpToWide(destinationPath);
			size_t pos = wdestinationPath.find(L'\\');
			while (pos != wstring::npos)
			{
				wstring folder = wdestinationPath.substr(0, pos);					
				CreateDirectory(folder.c_str(), NULL);
				pos = wdestinationPath.find(L'\\', pos+1);
			}
			try{
				File f( destinationPath, File::WRITE, File::TRUNCATE | File::CREATE);
				f.write( out.get(), outSize);
				return true;
			}catch(const FileException& e)
			{
				LogManager::getInstance()->message(STRING(AUTOUPDATE) + " " + STRING(ERROR_STRING) +"[" + e.getError() + "]");
				::MessageBox(0, Text::toT(e.getError()).c_str(), CTSTRING(AUTOUPDATE_ERROR), MB_ICONSTOP);
				return false;
			}
		}
	}
	return false;
}

string 
AutoUpdate::GetTempFolderForUpdate(const string &version)
{
	string tempFolder = Util::getTempPath();
	AppendPathSeparator(tempFolder);
	tempFolder+="ViewlinkDC_Update";
	wstring wtempFolder = Text::acpToWide(tempFolder);
	CreateDirectory(wtempFolder.c_str(), NULL);
	//	return Util::emptyString;

	AppendPathSeparator(tempFolder);
	tempFolder+=version;

	string tempFolderPath = tempFolder;
	FileFindIter end;
	bool bFolderfound  = true;
	int i = 0;
	do {
		i++;
		tempFolderPath = tempFolder + "_" + Util::toString(i);
		FileFindIter fiter(tempFolderPath);
		bFolderfound = (fiter != end );
		if (!bFolderfound)
		{
			wstring wtempFolderPath = Text::acpToWide(tempFolderPath);
			if (CreateDirectory(wtempFolderPath.c_str(), NULL) == NULL)
				bFolderfound = true;
		}
		if (i == 999){
			tempFolderPath = Util::emptyString;
			break;
		}
	} while ( bFolderfound );


	return tempFolderPath;
}

bool 
AutoUpdate::verifyUpdate(byte* sig, size_t& sig_len, string& data, size_t& dataSize)
{
	if (sig_len<=0 || dataSize<=0)
		return false;
	// Get Public Key
	RSA* r = NULL; 
	int rc = -1;
	do
	{
		// Make SHA hash
		SHA_CTX sha_ctx = { 0 };
		byte digest[SHA_DIGEST_LENGTH];

		rc = SHA1_Init(&sha_ctx);
		if (rc != 1)
			break;
		rc = SHA1_Update(&sha_ctx, data.c_str(), data.length());
		if (rc != 1)
			break;
		rc = SHA1_Final(digest, &sha_ctx);
		if (rc != 1)
			break;

		// Extract Key
		const byte* p = sViewlinkDCPublicKey;
		r = d2i_RSAPublicKey(NULL, &p, sizeof(sViewlinkDCPublicKey));


		rc = RSA_verify(NID_sha1, digest, sizeof(digest), sig, sig_len, r);
		if (rc != 1)
			break;


	}while (false);
	if ( r ) RSA_free(r); r = NULL;

	if (rc == 1)
	{
		return true;
	}

	return false;
}

#define UPDATE_FILEDOWNLOAD_B "Update5_beta.xml"
#define UPDATE_SIGNFILEDOWNLOAD_B "Update5_beta.sign"
#define UPDATE_DESCRIPTION_B "Update5_beta.rtf"

#define UPDATE_FILEDOWNLOAD_R "Update5.xml"
#define UPDATE_SIGNFILEDOWNLOAD_R "Update5.sign"
#define UPDATE_DESCRIPTION_R "Update5.rtf"



std::string AutoUpdate::UPDATE_FILEDOWNLOAD()
{
	return BOOLSETTING(AUTOUPDATE_TO_BETA) ? UPDATE_FILEDOWNLOAD_B : UPDATE_FILEDOWNLOAD_R;
}

std::string AutoUpdate::UPDATE_SIGNFILEDOWNLOAD()
{
	return BOOLSETTING(AUTOUPDATE_TO_BETA)?UPDATE_SIGNFILEDOWNLOAD_B : UPDATE_SIGNFILEDOWNLOAD_R;
}
std::string AutoUpdate::UPDATE_DESCRIPTION()
{
	return BOOLSETTING(AUTOUPDATE_TO_BETA)?UPDATE_DESCRIPTION_B : UPDATE_DESCRIPTION_R;
}

std::string AutoUpdate::getAUTOUPDATE_SERVER_URL()
{
	// SSA - get custom URL if need
	if (BOOLSETTING(AUTOUPDATE_USE_CUSTOM_URL))
	{
		std::string retValue = SETTING(AUTOUPDATE_SERVER_URL);
		if (!retValue.empty())
			return retValue;
	}

	return BOOLSETTING(AUTOUPDATE_TO_BETA)? UPDATE_BETA_URL : UPDATE_RELEASE_URL;
}

AutoUpdateFile AutoUpdate::ParseNode(XMLParser::XMLNode& Node)
{
	AutoUpdateFile file;
	wstring wName  = Node.getAttributeOrDefault(L"Name");
	wstring wPath  = Node.getAttributeOrDefault(L"Path");
	wstring wSys   = Node.getAttributeOrDefault(L"Sys");
	wstring wPack  = Node.getAttributeOrDefault(L"Pack");
	wstring wSize  = Node.getAttributeOrDefault(L"Size");
	wstring wTTH   = Node.getAttributeOrDefault(L"TTH");
	wstring wPackedSize  = Node.getAttributeOrDefault(L"PackedSize",L"0");
	wstring wUrl   = Node.getAttributeOrDefault(L"Url");

	file.m_sName = Text::wideToAcp(wName);
	file.m_sPath = Text::wideToAcp(wPath);
	if (wSys.empty())
		file.m_sys = AutoUpdateFile::xALL;
	else if (wSys == L"x86")
		file.m_sys = AutoUpdateFile::x86;
	else if (wSys == L"x64")
		file.m_sys = AutoUpdateFile::x64;
	else 
		file.m_sys = AutoUpdateFile::OsUnknown;
	if (wPack.empty())
		file.m_packer = AutoUpdateFile::Unpacked;
	else if (wPack == L"zip")
		file.m_packer = AutoUpdateFile::Zip;
	else if (wPack == L"bz2")
		file.m_packer = AutoUpdateFile::BZip2;
	else
		file.m_packer = AutoUpdateFile::PackUnknown;

	file.m_size =  Util::toInt64( Text::wideToAcp(wSize) );
	file.m_sTTH = Text::wideToAcp(wTTH);
	file.m_sDownloadURL = Text::wideToAcp(wUrl);
	if (file.m_packer == AutoUpdateFile::Unpacked)
		file.m_packedSize = file.m_size;
	else
		file.m_packedSize = Util::toInt64( Text::wideToAcp(wPackedSize) );

	return file;
}